package com.zeushotfix.wrap;
import android.app.ActivityManager;
import android.app.Application;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.os.Process;
import android.text.TextUtils;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import dalvik.system.DexClassLoader;
/**
* 补丁管理类,主要负责加载版本管理
* 如果软件被加固,请将除了InstallHotfixService之外的其他代码放到不加固的包名下
* 由此框架来加载加固的壳
* Created by huangjian on 2017/2/15.
*/
final class HotfixLoaderUtil {
private static final String HOTFIX_NEW_PATHINFO_PATH = "newPathinfo"; //主进程正在使用的安装信息的存储文件名
private static final String HOTFIX_OLD_PATHINFO_PATH = "oldPathinfo"; //非主进程正在使用的安装信息的存储文件名
private static final String APPLICATION_NAME = "android.app.Application"; //加固的application,第三方使用需要修改
private static final String APPLICATION_NAME_DEBUG = "android.app.Application"; //原始apk的application,用来普通调试用的,第三方使用需要修改
/**
* 初始化补丁框架
*
* @param applicationProxy
* @param context
* @return true表明加载了补丁框架, false表明没有加载补丁框架
*/
protected static Application attachBaseContext(Application applicationProxy, final Context context) {
Util.createDir(Util.getInsideHotfixPath(context));
Util.createDir(Util.getSdcardHotfixPath(context));
//判断是否是主进程
boolean isMainProcess = true;
if (!context.getPackageName().equals(getCurProcessName(context))) {
isMainProcess = false;
}
final String pathInfo = readPathInfo(context, isMainProcess);
//当前Resouces指向的apk为补丁文件夹即当前已经加载过补丁了,防止认为多次调用,或者如果补丁apk不存在,直接退出
if (Util.isEmpty(pathInfo) ||
context.getPackageResourcePath().equals(Util.getHotfixApkPath(context, pathInfo)) ||
!Util.fileExists(Util.getHotfixApkPath(context, pathInfo))) {
return loadApplication(applicationProxy, context);
}
HotfixMeta HotfixMeta = getHotfixMeta(context, pathInfo);
HotfixMeta currentMeta = getCurrentApkMeta(context);
//校验配置信息是否支持加载补丁
if (HotfixMeta == null ||
currentMeta == null ||
HotfixMeta.getVersion() < 1 ||
HotfixMeta.getVersionCode() < 1 ||
HotfixMeta.getVersion() != currentMeta.getVersion() ||//如果补丁的version code小于当前版本则不加载补丁
currentMeta.getVersionCode() > HotfixMeta.getVersionCode()) {
//补丁文件损坏或者补丁版本比宿主还低,这时清理清理补丁
if (isMainProcess) {
new Thread(new Runnable() {
@Override
public void run() {
//清空补丁目录下所有文件和文件夹
Util.deleteFilesInDirectory(Util.getSdcardHotfixPath(context));
Util.deleteFilesInDirectory(Util.getInsideHotfixPath(context));
}
}).start();
}
return loadApplication(applicationProxy, context);
}
//如果优化后的odex文件不存在,则去优化odex文件,且当前不加载补丁,防止出现anr
if(!new File(Util.getInsideHotfixVersionPath(context, pathInfo)).exists()){
new Thread(new Runnable() {
@Override
public void run() {
new DexClassLoader(Util.getHotfixApkPath(context, pathInfo),
Util.getInsideHotfixVersionPath(context, pathInfo),
"",
context.getClassLoader().getParent());
}
}).start();
return loadApplication(applicationProxy, context);
}
ApplicationInfo applicationInfo = context.getApplicationInfo();
Object packageInfo = Util.getField(context, "mPackageInfo");
try {
//设置当前的ClassLoader为补丁apk创建的ClassLoader
DexClassLoader dexClassLoader = new DexClassLoader(Util.getHotfixApkPath(context, pathInfo),
Util.getInsideHotfixVersionPath(context, pathInfo),
Util.getNativeLibPath(context, pathInfo),
context.getClassLoader().getParent());
Util.setField(packageInfo, "mClassLoader", dexClassLoader);
// 设置mResDir为补丁apk的路径,如果mResources为null,则系统会使用该地址创建一个Resources
Util.setField(packageInfo, "mResDir", Util.getHotfixApkPath(context, pathInfo));
// 设置mAppDir为补丁apk的路径,它影响context.getPackageCodePath()结果
Util.setField(packageInfo, "mAppDir", Util.getHotfixApkPath(context, pathInfo));
//设置mLibDir影响存放so文件路径地址
Util.setField(packageInfo, "mLibDir", Util.getNativeLibPath(context, pathInfo));
//设置mResources为null,以便系统使用之前设置的mResDir创建新的Resources
Util.setField(packageInfo, "mResources", null);
applicationInfo.sourceDir = Util.getHotfixApkPath(context, pathInfo);
applicationInfo.publicSourceDir = Util.getHotfixApkPath(context, pathInfo);
applicationInfo.nativeLibraryDir = Util.getNativeLibPath(context, pathInfo);
} catch (Throwable e) {
e.printStackTrace();
}
// 获取mActivityThread
Object activityThread = Util.getField(packageInfo, "mActivityThread");
// 设置mResources参数
Class<? extends Object> packInfoClass = packageInfo.getClass();
Class[] arrayOfClass = new Class[1];
arrayOfClass[0] = activityThread.getClass();
// 让系统自己设置Resources,该Resources为补丁apk的Resources
try {
packInfoClass.getDeclaredMethod(
"getResources",
arrayOfClass).invoke(packageInfo,
new Object[]{activityThread});
} catch (Exception e) {
}
String versionName = HotfixMeta.getVersionName();
int versionCode = HotfixMeta.getVersionCode();
// 获取sPackageManager
Object packageManager;
try {
packageManager = activityThread.getClass()
.getMethod("getPackageManager", new Class[0])
.invoke(null, new Object[0]);
//因为packageManager是IBinder接口对象,这里可以设置动态代理
Object packageManagerPoxy = Proxy.newProxyInstance(
applicationProxy.getClass().getClassLoader(),
packageManager.getClass().getInterfaces(),
new PackageManagerInvocation(packageManager, context, versionCode, versionName, pathInfo));
Util.setField(activityThread, "sPackageManager", packageManagerPoxy);
Util.setField(applicationProxy.getPackageManager(), "mPM", packageManagerPoxy);
} catch (Exception exception1) {
exception1.printStackTrace();
}
final boolean finalIsMainProcess = isMainProcess;
new Thread(new Runnable() {
@Override
public void run() {
//降低线程优先级防止阻塞UI线程
Process.setThreadPriority(Process.THREAD_PRIORITY_LOWEST);
//进行签名校验,校验失败则杀死进程
try {
Signature[] hotfixSignature = Util.getApkSignature(Util.getHotfixApkPath(context, pathInfo), context);
Signature[] currentSignature = Util.getPackageSignature(context.getPackageName(), context);
if (!hotfixSignature[0].equals(currentSignature[0])) {
Process.killProcess(Process.myPid());
}
} catch (Exception e) {
Process.killProcess(Process.myPid());
}
if (finalIsMainProcess) {
//记录主进程当前使用补丁版本
Util.writeString(Util.getOtherPathInfoPath(context, HOTFIX_NEW_PATHINFO_PATH), pathInfo);
//如果当前加载的补丁版本与其他进程的补丁版本不一致,则重启其他进程
if (Util.fileExists(Util.getOtherPathInfoPath(context, HOTFIX_OLD_PATHINFO_PATH))) {
String oldPathInfo = Util.readString(Util.getOtherPathInfoPath(context, HOTFIX_OLD_PATHINFO_PATH));
if (Util.isEmpty(oldPathInfo) && !pathInfo.equals(oldPathInfo)) {
Util.resetPackagerOtherProcess(context);
}
}
//主进程则清除其他无效的补丁以释放存储空间
clearOldHotfix(context, pathInfo);
} else {
//记录非主进程使用的版本,这个值这个时候是等于主进程加载的补丁的版本
//如果主进程使用新版本补丁之后就会读这个值,如果不一致就会重启非主进程
Util.writeString(Util.getOtherPathInfoPath(context, HOTFIX_OLD_PATHINFO_PATH), pathInfo);
}
}
}).start();
return loadApplication(applicationProxy, context);
}
/**
* 加载真实的application
*
* @param applicationProxy
* @param context
* @return
*/
protected static Application loadApplication(Application applicationProxy, Context context) {
Application realApplication = loadApplication(applicationProxy, context, APPLICATION_NAME);
if (realApplication == null) {
realApplication = loadApplication(applicationProxy, context, APPLICATION_NAME_DEBUG);
}
return realApplication;
}
/**
* 加载真实的application
*
* @param applicationProxy 当前代理的application
* @param context
* @param applicationName 真实的application的完整名字,包括包名
* @return
*/
private static Application loadApplication(Application applicationProxy, final Context context, String applicationName) {
Application delegateApplication;
ArrayList<Application> allApplications;
Class<?> delegateClass;
try {
delegateClass = context.getClassLoader().loadClass(applicationName);
delegateApplication = (Application) delegateClass.newInstance();
} catch (Exception e1) {
return null;
}
Object mPackageInfo = Util.getField(context, "mPackageInfo");
// 获取mActivityThread
Object mActivityThread = Util.getField(mPackageInfo, "mActivityThread");
Util.setField(context, "mOuterContext", delegateApplication);
Util.setField(mPackageInfo, "mApplication", delegateApplication);
Util.setField(mActivityThread, "mInitialApplication", delegateApplication);
allApplications = (ArrayList<Application>) Util.getField(mActivityThread, "mAllApplications");
if (allApplications != null) {
for (int i = 0; i < allApplications.size(); i++) {
if (allApplications.get(i) == applicationProxy) {
allApplications.set(i, delegateApplication);
}
}
}
try {
Method attach = Application.class.getDeclaredMethod("attach", new Class[]{Context.class});
attach.setAccessible(true);
attach.invoke(delegateApplication, new Object[]{context});
} catch (Throwable e) {
e.printStackTrace();
}
return delegateApplication;
}
/**
* 清除老版本的补丁
*
* @param context
*/
private static void clearOldHotfix(Context context, String pathInfo) {
if (TextUtils.isEmpty(pathInfo)) return;
File pluginDir = new File(Util.getSdcardHotfixPath(context));
if (pluginDir.exists() && pluginDir.isDirectory()) {
File[] list = pluginDir.listFiles();
if (list == null) return;
for (File f : list) {
String fileFullName = f.getName();
if (!fileFullName.equalsIgnoreCase(pathInfo)) {
Util.deleteDirectorySafe(f);
File cacheFile = new File(Util.getInsideHotfixVersionPath(context, fileFullName));
if (cacheFile.exists()) {
cacheFile.delete();
}
}
}
}
}
/**
* 获取当前进程的进程名
*
* @param context
* @return
*/
private static String getCurProcessName(Context context) {
int pid = Process.myPid();
ActivityManager mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningAppProcessInfo appProcess : mActivityManager.getRunningAppProcesses()) {
if (appProcess.pid == pid) {
return appProcess.processName;
}
}
return null;
}
/**
* 读取补丁的安装路径信息
*
* @param context
* @param isMainProcess 是否是主进程
* @return
*/
private static String readPathInfo(Context context, boolean isMainProcess) {
String result = null;
String path;
if (!isMainProcess && Util.fileExists(Util.getOtherPathInfoPath(context, HOTFIX_NEW_PATHINFO_PATH))) {//如果是主进程则读取最后一次安装的补丁
path = Util.getOtherPathInfoPath(context, HOTFIX_NEW_PATHINFO_PATH);
} else {//如果不是主进程则读取上次主进程加载的补丁
path = Util.getPathInfoPath(context);
}
if (!Util.fileExists(path)) {
return result;
}
result = Util.readString(path);
return result;
}
/**
* 获取某个版本补丁的配置信息
*
* @param context
* @param pathInfo
* @return
*/
private static HotfixMeta getHotfixMeta(Context context, String pathInfo) {
HotfixMeta result = null;
if (!Util.fileExists(Util.getMetaFilePath(context, pathInfo))) {
return result;
}
String metaString = Util.readString(Util.getMetaFilePath(context, pathInfo));
result = convertToHotfixMeta(metaString);
return result;
}
/**
* 把字符串转换为配置信息类
*
* @param meta
* @return
*/
private static HotfixMeta convertToHotfixMeta(String meta) {
try {
JSONObject json = new JSONObject(meta);
int verion = json.optInt("version", -1);
int versionCode = json.optInt("versionCode", -1);
String versionName = json.optString("versionName", "");
HotfixMeta HotfixMeta = new HotfixMeta();
HotfixMeta.setVersion(verion);
HotfixMeta.setVersionCode(versionCode);
HotfixMeta.setVersionName(versionName);
return HotfixMeta;
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
/**
* 获取当前宿主的配置信息
*
* @param context
* @return
*/
private static HotfixMeta getCurrentApkMeta(Context context) {
HotfixMeta HotfixMeta = null;
try {
PackageInfo localPackageInfo = context.getPackageManager()
.getPackageInfo(context.getPackageName(),
PackageManager.GET_META_DATA);
if (localPackageInfo != null && localPackageInfo.applicationInfo != null && localPackageInfo.applicationInfo.metaData != null) {
HotfixMeta = new HotfixMeta();
HotfixMeta.setVersion(localPackageInfo.applicationInfo.metaData.getInt(Util.HOTFIX_VERISON, 0));
HotfixMeta.setVersionCode(localPackageInfo.versionCode);
HotfixMeta.setVersionName(localPackageInfo.versionName);
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return HotfixMeta;
}
}